package com.anycc.cloud.core.service.impl;

import com.anycc.cloud.common.dto.easyui.Tree;
import com.anycc.cloud.common.dto.easyui.TreeBuilder;
import com.anycc.cloud.common.dto.query.PagingRequest;
import com.anycc.cloud.common.dto.query.PagingResponse;
import com.anycc.cloud.common.exception.AnyccRuntimeException;
import com.anycc.cloud.common.util.DynamicQuery;
import com.anycc.cloud.common.util.PagingUtil;
import com.anycc.cloud.common.util.PatternUtil;
import com.anycc.cloud.core.entity.Module;
import com.anycc.cloud.core.entity.Permission;
import com.anycc.cloud.core.entity.Role;
import com.anycc.cloud.core.repository.ModuleRepository;
import com.anycc.cloud.core.service.ModuleService;
import com.anycc.cloud.core.service.RoleService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.anycc.cloud.common.constant.CacheNameConstant.FIELD_CACHE_BREADCRUMB;
import static com.anycc.cloud.common.constant.CacheNameConstant.FIELD_CACHE_MENU;
import static com.anycc.cloud.common.constant.CacheNameConstant.FIELD_CACHE_MODULE_TREE;

/**
 * module service impl
 */
@Service
public class ModuleServiceImpl implements ModuleService {
    private TreeBuilder<Module> moduleTreeBuilder = new ModuleServiceImpl.ModuleTreeBuilder();

    @Autowired
    private ModuleRepository moduleRepository;
    @Autowired
    private RoleService roleService;

    @Override
    public Module create(Module module) {
        checkAndUpperCaseSn(module);
        initPermissionModule(module);
        return moduleRepository.save(module);
    }

    @Override
    @Cacheable(value = FIELD_CACHE_MODULE_TREE, key = "'model' +'-'+ #root.method.name")
    public List<Tree> tree() {
        return moduleTreeBuilder.buildTreeList(moduleRepository.findAll(new Sort(Sort.Direction.ASC, "priority")));
    }

    @Override
    public PagingResponse<Module> findByPaging(Module module, PagingRequest pagingRequest) {
        Page<Module> page = moduleRepository
                .findAll(new DynamicQuery<Module>().build((predicates, root, query, cb) -> {
                    if (StringUtils.isNotBlank(module.getName())) {
                        predicates.add(cb.like(root.get(Module.FIELD_NAME), PatternUtil.trimLike(module.getName())));
                    }
                    Optional.ofNullable(module.getParent()).ifPresent(parent ->
                            predicates.add(cb.equal(root.get(Module.FIELD_PARENT).get(Module.FIELD_ID), parent.getId()))
                    );
                }), PagingUtil.buildPageRequest(pagingRequest));
        return PagingUtil.buildPagingResponse(pagingRequest, page);
    }

    @Override
    @Transactional
    @CacheEvict(value = {FIELD_CACHE_BREADCRUMB,FIELD_CACHE_MENU,FIELD_CACHE_MODULE_TREE},allEntries=true)//allEntries表示清除该key下所有元素
    public void delete(String id) {
        Assert.notNull(id, "删除模块时，模块ID不能为空");
        Module module = moduleRepository.findById(id).orElse(null);
        if (module == null) {
            throw new AnyccRuntimeException("core.exceptions.module.not_existed");
        } else if (!module.getChildren().isEmpty()) {
            throw new AnyccRuntimeException("core.exceptions.module.delete_has_children");
        } else {
            roleService.deletePermissionsByModuleId(id);
            moduleRepository.deleteById(id);
        }
    }

    @Override
    public Module findById(String id) {
        return moduleRepository.findById(id).orElse(new Module());
    }

    @Override
    @CacheEvict(value =  {FIELD_CACHE_BREADCRUMB,FIELD_CACHE_MENU,FIELD_CACHE_MODULE_TREE},allEntries=true)//allEntries表示清除该key下所有元素
    public Module update(Module module) {
        checkAndUpperCaseSn(module);
        Assert.notNull(module.getId(), "更新模块时，模块ID不能为空");
        initPermissionModule(module);
        Module dbModule = moduleRepository.findById(module.getId()).orElse(null);
        Assert.notNull(dbModule, "查找到的Module实体为空");
        List<String> deletedPermissionIds = handlerDeletedPermissionId(dbModule.getPermissions(), module.getPermissions());
        if (!deletedPermissionIds.isEmpty()) {
            roleService.deletePermissions(deletedPermissionIds);
        }
        module.setChildren(dbModule.getChildren());
        return moduleRepository.save(module);
    }

    @Override
    public List<Tree> permissionTree() {
        return moduleTreeBuilder.buildTreeList(moduleRepository.findAll(new Sort(Sort.Direction.ASC, "priority")), true);
    }

    @Override
    public List<Tree> permissionTreeSelectedByRoleId(String roleId) {
        Role role = roleService.findById(roleId);
        Assert.notNull(role, "角色不能为空!");
        if (role.getPermissions().isEmpty()) {
            return this.permissionTree();
        } else {
            String[] checkedNodeIds = new String[role.getPermissions().size()];
            for (int i = 0; i < role.getPermissions().size(); i++) {
                checkedNodeIds[i] = role.getPermissions().get(i).getId();
            }
            return this.moduleTreeBuilder.buildTreeList(moduleRepository.findAll(new Sort(Sort.Direction.ASC, "priority")), true, checkedNodeIds);
        }
    }


    private void checkAndUpperCaseSn(Module module) {
        if (module != null && !StringUtils.isBlank(module.getSn())) {
            module.setSn(module.getSn().toUpperCase());
            Module dbModule = moduleRepository.findBySn(module.getSn());
            if (dbModule != null && !dbModule.getId().equals(module.getId())) {
                throw new AnyccRuntimeException("core.exceptions.module.sn_existed，" + module.getSn());
            }
        } else {
            throw new AnyccRuntimeException("core.exceptions.module.sn_required");
        }
    }

    private void initPermissionModule(Module module) {
        module.getPermissions().forEach(permission -> {
            if (permission == null || StringUtils.isBlank(permission.getSn())) {
                throw new AnyccRuntimeException("core.exceptions.permission.sn_required");
            }
            permission.setSn(permission.getSn().toUpperCase());
            if (permission.getModule() == null) {
                permission.setModule(module);
            }
        });
    }

    private List<String> handlerDeletedPermissionId(List<Permission> dbPermissions, List<Permission> newPermissions) {
        List<String> delPermissionIds = new ArrayList<>();

        for (Permission dbPermission : dbPermissions) {
            boolean isDeleted = true;
            for (Permission newPermission : newPermissions) {
                if (dbPermission.getId().equals(newPermission.getId())) {
                    isDeleted = false;
                    break;
                }
            }
            if (isDeleted) {
                delPermissionIds.add(dbPermission.getId());
            }
        }

        return delPermissionIds;
    }

    private class ModuleTreeBuilder extends TreeBuilder<Module> {//工厂方法模式子类

        private ModuleTreeBuilder() {
        }

        protected Module getParent(Module entity) {
            return entity.getParent();
        }

        protected List<Module> getChildren(Module entity) {
            return entity.getChildren();
        }

        protected String getNodeId(Module entity) {
            return entity.getId();
        }

        protected String getNodeText(Module entity) {
            return entity.getName();
        }

        protected void addExtraInfo(Tree treeNode, Module entity) {
            entity.getPermissions().forEach(permission ->
                    treeNode.getChildren().add(buildTreeNode(permission.getId(), permission.getName()))
            );
        }
    }
}
